home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 …ember: Reference Library / Dev.CD Dec 94.toast / What's New? / Sample Code / Snippets Update / Simple Imagecompressor ƒ / CompressImageTest.c next >
Encoding:
Text File  |  1994-10-21  |  18.5 KB  |  598 lines  |  [TEXT/KAHL]

  1. // routines for working with the image compressor component,
  2. // this stuff is culled from an earlier sample I did, so I think
  3. // it is probably better commented than the rest of this stuff!!
  4. //
  5. // However, this code was origially developed to work with the Image
  6. // compression stuff in QuickTime 1.0, and uses the calls that were
  7. // available then (with a couple of minor exceptions).  I may need to modernise
  8. // this at some point.
  9. //
  10. // Nick Thompson, June '94
  11. //
  12. //    Copyright:    © 1994 by Apple Computer, Inc., all rights reserved.
  13.  
  14. #include <ImageCompression.h>
  15. #include <QuickTimeComponents.h>
  16. #include "CompressImageTest.h"
  17.  
  18. #include "GetQTCompressedPict.h"
  19.  
  20.  
  21. //--------------------------------------------------------------------------------------
  22. //                    
  23. //    Name            CheckError
  24. //
  25. //    Arguments        OSErr error                the code returned by a toolbox routine
  26. //                    Str255 displayString    a string to display in the debugger
  27. //
  28. //    Description        ched the code in error, if != noErr, enter the debugger
  29. //
  30. //    Returns            nothing
  31. //
  32. //    Side Effects    may cause execution to terminate
  33. //
  34. //--------------------------------------------------------------------------------------
  35.  
  36. void     CheckError(OSErr error, Str255 displayString)
  37. {
  38.         if (error == noErr) return;
  39.         if (displayString[0] > 0) 
  40.             DebugStr(displayString);
  41.         ExitToShell();
  42. }
  43.  
  44.  
  45. //--------------------------------------------------------------------------------------
  46. //                    
  47. //    Name            CompressPixMap
  48. //
  49. //    Arguments        PixMapHandle thePixMap - handle to the pixmap to compress, passed from
  50. //                                             the offscreen world
  51. //                    Rect *theFrame - rectangle describing the bounds of the image
  52. //                    SCParams *params - from the user, use the std comression dialog to 
  53. //                                       generate the required compression settings.
  54. //                    ComponentInstance ci - the standard component
  55. //
  56. //    Description        This is the business end of the app.  Try adding JPEG compression
  57. //                    to an app without QuickTime :->  Take an image stored in an offscreen 
  58. //                    bitmap and apply compression as described by the fields in the
  59. //                    SCParams record.
  60. //
  61. //    Returns            On sucessful completion return a handle to the compressed picture..
  62. //
  63. //    Side Effects    using the setting provided by the user compress the image.
  64. //                    mat allocate storahe for the image and the image description.
  65. //                    Note that this routine may attemt to allocate storage for the compressed
  66. //                    image.  Whilst the minimum amount required will usually be less
  67. //                    than the size of the pixmap passed, for large and or deep images the
  68. //                    storage requirement may be large.
  69. //
  70. //--------------------------------------------------------------------------------------
  71.  
  72.  
  73. PicHandle CompressPixMap(     PixMapHandle thePixMap, 
  74.                             Rect *theFrame,     
  75.                             SCParams *params,     
  76.                             ComponentInstance ci)
  77. {
  78.     // local variables
  79.     PicHandle                myPic ;
  80.     OpenCPicParams            myOpenCPicparams ;
  81.     long                    maxCompressedSize = 0;        // the maximum possible size of the compressed image
  82.     OSErr                    result = noErr;                // the return code of this function
  83.     Handle                    compressedDataH    ;            // handle to some store for our compressed image
  84.     Ptr                        data ;                        // pointer to the above, 32 bit clean
  85.     ImageDescriptionHandle    ImageDescH ;                // image description handle
  86.  
  87.     long                    length ;
  88.     long                    dummy ;
  89.     GrafPtr                    currentPort ;
  90.     int                        index ;
  91.     PixMapHandle            myPix ;
  92.     
  93.     // calculate the maximum compression size for the picture.
  94.     // We need to caculate the max possible size for the image,
  95.     // so that we can allocate storage for the call to compress image.
  96.     // The value returned in maxCompressedSize is used to size a Handle.
  97.     
  98.     result = GetMaxCompressionSize( 
  99.                 thePixMap,
  100.                 theFrame,
  101.                 params->depth,                    // pixel depth to compress image at 
  102.                 params->spatialQuality,            // spatial quality to compress image at 
  103.                 params->theCodecType,            // type of codec to use 'jpeg','rpza', etc. 
  104.                 params->theCodec,                // codec to use either in general 
  105.                                                 // terms (bestSpeed, bestFidelity,etc.) 
  106.                 &maxCompressedSize) ;            // how big will the pict be?? 
  107.                 
  108.     
  109.     if (result != noErr) {
  110.         CheckError( result, "\pGetMaxCompressionSize failed" );
  111.         return nil;
  112.     }
  113.         
  114.     // Allocate storage for the compressed image.  We use the value
  115.     // returned in maxCompressedSize, from a call to GetMaxCompressionSize
  116.     // earlier on.
  117.     
  118.     if(( compressedDataH = NewHandle( maxCompressedSize ) ) == nil )
  119.         result = MemError() ;    // why did we get a nil handle back??
  120.         
  121.     if( result != noErr ) {
  122.         
  123.         // handle the error and bail out.
  124.         
  125.         CheckError( result, "\pfailed to allocate memory for the pict" );
  126.         return nil;
  127.         
  128.     }
  129.     
  130.     // move the handle high and lock it down.
  131.         
  132.     MoveHHi( (Handle)compressedDataH ) ;
  133.     HLock( (Handle) compressedDataH ) ;
  134.     
  135.     // make sure we are using a 32bit clean address for the call to comress image
  136.     
  137.     data = StripAddress( *compressedDataH ) ;
  138.     
  139.     // allocate some storage for the image description handle.
  140.     
  141.     if(( ImageDescH = (ImageDescriptionHandle)NewHandle(4) ) == nil )
  142.         result = MemError() ;    // why did we get a nil handle back??
  143.         
  144.     if( result != noErr ) {
  145.  
  146.         // free the storage for the data
  147.         
  148.         DisposHandle((Handle)compressedDataH) ;    
  149.         
  150.         // handle the error and bail out.
  151.         
  152.         CheckError( result, "\pfailed to allocate memory for the image desc handle" );
  153.         return nil;
  154.         
  155.     }
  156.  
  157.     
  158.     // should *always* lock the images pix map down, before messing with it.
  159.     
  160.     LockPixels( thePixMap ) ;
  161.     
  162.     // Call the image compression manager to compress the image.
  163.     // This is all a bit wierd, the data in the parameter “data”
  164.     // is in a private QuickTime ICM format.
  165.     // To get the compressed pict, we need to open a Picture and
  166.     // decompress the image into it.
  167.     
  168.     result = FCompressImage(
  169.                 thePixMap,
  170.                 theFrame,
  171.                 params->depth,                    // pixel depth to compress image at 
  172.                 params->spatialQuality,            // spatial quality to compress image at 
  173.                 params->theCodecType,            // type of codec to use 'jpeg','rpza', etc. 
  174.                 params->theCodec,                // codec to use either in general terms (bestSpeed, bestFidelity,etc.) 
  175.                 nil,                            // custom clut to use, nil for none 
  176.                 !codecFlagWasCompressed,        // the picture wasn't compressed already (as far as we know) 
  177.                 0L,                                // we have not specified a data unloading proc, so set this to nil 
  178.                 nil,                             // no flush proc 
  179.                 nil,                                // we dont provide progress information while compressing 
  180.                 ImageDescH,                        // storage for the image descriptor
  181.                 data ) ;                        // where to put the compressed image
  182.                 
  183.     if( result != noErr ) {
  184.     
  185.         // free the storage for the data
  186.         
  187.         DisposHandle((Handle)compressedDataH) ;            
  188.         DisposHandle((Handle)ImageDescH) ;    
  189.  
  190.         // did we fail?, handle the failure…
  191.         
  192.         CheckError( result, "\pfailed to compress the picture" );
  193.         return nil;
  194.     
  195.     }
  196.         
  197.     // unlock the pix map.                                
  198.     
  199.     UnlockPixels( thePixMap) ;
  200.     
  201.     myPix = (PixMapHandle)compressedDataH ;
  202.     
  203.     GetPort( ¤tPort ) ;
  204.     
  205.     myOpenCPicparams.srcRect = *theFrame ;
  206.     myOpenCPicparams.hRes = (**ImageDescH).hRes ;
  207.     myOpenCPicparams.vRes = (**ImageDescH).vRes ;
  208.     myOpenCPicparams.version = -2 ;
  209.     
  210.     myPic = OpenCPicture( &myOpenCPicparams ) ;
  211.  
  212.  
  213.     // DecompressImage will decompress the data in data, and produce a compressed
  214.     // picture.  Why call decompresss image here?  The data stored in our “data” handle 
  215.     // is not of a structure we know about, as it is a private QuickTime data type.
  216.     // Data holds QuickTime compressed information, we need to call decompressImage
  217.     // do decompress the image data, held in quicktime format, into a PICT.
  218.     // one side effect of this call is that the PICT will be a compressed PICT, which
  219.     // is what we want.
  220.     
  221.     result = DecompressImage(    data,
  222.                                 ImageDescH,
  223.                                 thePixMap,
  224.                                 theFrame,
  225.                                 theFrame,
  226.                                 srcCopy,
  227.                                 nil ) ;
  228.  
  229.       ClosePicture() ;
  230.  
  231.     HUnlock( (Handle) compressedDataH ) ;
  232.     
  233.     // free the storage for the data
  234.     
  235.     DisposHandle((Handle)compressedDataH) ;            
  236.     DisposHandle((Handle)ImageDescH) ;    
  237.  
  238.     
  239.     return myPic ;        // should be noErr
  240.     
  241. }
  242.  
  243. //--------------------------------------------------------------------------------------
  244. //                    
  245. //    Name            InitialiseSCParams
  246. //
  247. //    Arguments        SCParams *params - ref to an SCParams record to fill in
  248. //
  249. //    Description        fill in the default compression parameters.  a real application
  250. //                    would read this stuff from the user prefs file
  251. //
  252. //    Returns            nothing
  253. //
  254. //    Side Effects        none, make sure you pass a reference to the record to fill in!!
  255. //
  256. //--------------------------------------------------------------------------------------
  257.  
  258.  
  259. void InitialiseSCParams(SCParams *params)
  260. {
  261.  
  262.     //    Fill in default compression settings.  Note that because we
  263.     //    don't want motion compression settings (the scShowMotionSettings bit
  264.     //    is clear in params->flags), we don't need to initialize params->temporalQuality,
  265.     //    params->frameRate, or params->keyFrameRate.
  266.     
  267.     params->flags = 0;                                // we don't have any special requirements 
  268.     params->theCodecType = 'jpeg';                    // default to using JPEG compressor 
  269.     params->theCodec = anyCodec;                    // use any of the available JPEG compressors hh
  270.     params->spatialQuality = codecNormalQuality;    // default to normal quality 
  271.     params->depth = 0;                                // let the compression dialog pick
  272.                                                     // the best default depth
  273.                                                      
  274.  
  275. }
  276.  
  277.  
  278. //--------------------------------------------------------------------------------------
  279. //                    
  280. //    Name            GetCompressionSettings
  281. //
  282. //    Arguments        DocumentHdl documentHandle - our document data structure
  283. //                    SCParams *params - reference to the current compression settings
  284. //                    ComponentInstance ci - std compression component
  285. //
  286. //    Description        put up the standard compression dialog, get the compression settings
  287. //                    from the user, store them in the SCParams reference.
  288. //
  289. //    Returns            on successful completion noErr, on error an error code 
  290. //                    indicating the problem
  291. //
  292. //    Side Effects        will cause the std compression dialog to appear.
  293. //
  294. //--------------------------------------------------------------------------------------
  295.  
  296.  
  297. OSErr GetCompressionSettings(    WindowPtr            theWindow, 
  298.                                 SCParams             *params, 
  299.                                 ComponentInstance    ci )
  300. {
  301.     ComponentResult        result = noErr;
  302.     Point                where;
  303.     GWorldPtr            theNewWorld ;
  304.     PixMapHandle        offPixMap ;
  305.  
  306.     
  307.     //            
  308.     //    Tell Standard Compression to use the chosen window 
  309.     //    for the test image. Standard compression will also 
  310.     //    accept pict handles for the test image using the 
  311.     //    SCSetTestImagePictHandle call.  A number of file
  312.     //    based routines are available for compressing PICT
  313.     //    files "in place".   The pict file call is often 
  314.     //    more convenient, but we want to demo offscreen
  315.     //    manipulations for this sample
  316.     
  317.     // get the GWorld from the window refcon
  318.     theNewWorld = (GWorldPtr)GetWRefCon ( theWindow );
  319.     offPixMap = GetGWorldPixMap( theNewWorld ) ;
  320.  
  321.     LockPixels( offPixMap ) ;
  322.     
  323.     // SCSetTestImagePixMap is defined in the StdCompression glue
  324.     // this just gets an area of the image to display as a small sample
  325.     // of what compression will look like in the standard compression dialog
  326.     
  327.     result = SCSetTestImagePixMap(
  328.                 ci,                                // our instance of the standard compression component 
  329.                 offPixMap,                        // pixMap for source picture 
  330.                 nil,                            // we don't have rectangle of interest so use the entire image 
  331.                 scPreferScalingAndCropping);    // scale the test image down until reasonably small
  332.                                                 //    and then crop that to the test image box.
  333.                                                  
  334.     UnlockPixels( offPixMap ) ;
  335.  
  336.     if (result != noErr) {
  337.         CheckError( result, "\pSCSetTestImagePixMap failed" );
  338.         return result;
  339.     }
  340.     
  341.     
  342.     //    Get compression settings from the user, and provide them with a "Defaults"
  343.     //    button.  Note that if we were not implementing "Defaults" we could use the
  344.     //    shorter call "result = SCGetCompression(ci,¶ms,where);".  Giving the user
  345.     //    the "Defaults" option is probably not necessary for most applications that
  346.     //    would use the Standard Compression dialog, and is only shown as an example
  347.     //    of how implement a custom button.
  348.      
  349.     
  350.      where.h = where.v = -2;                     // already set to (-2,-2) above */
  351.     result = SCGetCompressionExtended(
  352.                 ci,                                // our instance of the standard compression component */
  353.                 params,                            // the default settings */
  354.                 where,                            // (-2,-2) means center dialog on the best device */
  355.                 nil,                            // we don't need to filter any dialog events */
  356.                 nil,                            // our hookProc for handling the custom button */
  357.                 0,                                // we don't have any data that needs to be passed to our hookProc */
  358.                 nil );                            // Name of the custom button we want added.  If we didn't want a
  359.                                                 // custom button, we'd pass in nil and no button would be shown.
  360.                                                 
  361.     
  362.     return result ;
  363.  
  364. }
  365.  
  366. //--------------------------------------------------------------------------------------
  367. //                    
  368. //    Name            GetOutputFileRef
  369. //
  370. //    Arguments        short *dstPictFRef - a reference to a short file descriptor
  371. //
  372. //    Description        Ask the user for a file to write the image to, using the new
  373. //                    style standard file routines.  If we aget a file to open from
  374. //                    the user, delete any existing file with that name, and create a 
  375. //                    new file.  Open the file and return a reference to it in the 
  376. //                    parameter dstPictFileRef.  Will return an OSErr if a system error
  377. //                    occurs.
  378. //                    
  379. //                    If the user cancels the standard file dialog, then will return
  380. //                    userCanceledErr and the file reference will be set to zero.  
  381. //
  382. //    Returns            operating system error code if an error happened, else return noErr.
  383. //
  384. //    Side Effects    may cause a dialog to be displayed, may open a file, may delete
  385. //                    a preexisting file (on user confirmation)
  386. //
  387. //--------------------------------------------------------------------------------------
  388.  
  389.  
  390. OSErr GetOutputFileRef(short    *dstPictFRef ) // GetOutputFileRefNum
  391. {
  392.     StandardFileReply    aPictSFR;    // new style Standard file record 
  393.     OSErr                result ;
  394.     SFTypeList     types = { 'PICT',0 };        // we are only interested in PICT files
  395.         
  396.     // check to see if we have the new style standard file package
  397.  
  398.     StandardPutFile( "\pSave PICT as:", "\pCompressed PICT", &aPictSFR);
  399.     if ( aPictSFR.sfGood  ) {
  400.         
  401.         // Delete the file of the same name, if one exits, 
  402.         // and create a new PICT file.
  403.         
  404.         FSpDelete(&aPictSFR.sfFile);
  405.         
  406.         result = FSpCreate( &aPictSFR.sfFile,'????','PICT', aPictSFR.sfScript);
  407.         if (result != noErr) {
  408.             return result;
  409.         }
  410.         
  411.         // open the data fork for writing
  412.         // ??? need to change the permissions here ???
  413.         
  414.         result = FSpOpenDF( &aPictSFR.sfFile, fsCurPerm, dstPictFRef);
  415.         if (result != noErr) {
  416.             return result;
  417.         }
  418.     }
  419.     else 
  420.         return userCanceledErr ;
  421.         
  422.     return noErr ;
  423. }
  424.  
  425.  
  426. //--------------------------------------------------------------------------------------
  427. //                    
  428. //    Name            WritePict
  429. //
  430. //    Arguments        PicHandle myPic - reference to the picture handle
  431. //                    short  dstPictFRef - file reference, to an open file for writing
  432. //
  433. //    Description        Given a handle to a picture in myPic and a file reference, 
  434. //                    write the image to the file.
  435. //
  436. //    Returns            On sucessful completion return noErr, else return an error code 
  437. //                    indicating the reason for the failure.
  438. //
  439. //    Side Effects        may write a byte stream to the file
  440. //
  441. //--------------------------------------------------------------------------------------
  442.  
  443. OSErr WritePict( PicHandle myPic, short  dstPictFRef )
  444. {
  445.      long                    length ;
  446.     long                    dummy ;
  447.     GrafPtr                    currentPort ;
  448.     int                        index ;
  449.     PixMapHandle            myPix ;
  450.     OSErr                    result ;
  451.  
  452.      // ok, myPic now contains a handle to the compressed picture
  453.                         
  454.     HLock( (Handle)(Handle)myPic);
  455.  
  456.    // set up the 512 byte header for a PICT file 
  457.     dummy = 0;
  458.     
  459.     for( index = 0; index < ( 512 / 4 ); index ++ ){
  460.         length = 4 ;
  461.            
  462.            if( (result = FSWrite(dstPictFRef, &length, &dummy)) != noErr ) {
  463.                // ErrorAlert( rErrorStrs, eCantWriteFile );
  464.             return result ;        
  465.         }
  466.     }                
  467.     
  468.        length = GetHandleSize( (Handle)myPic);
  469.       
  470.        if( (result = FSWrite(dstPictFRef, &length, *myPic)) != noErr ) {
  471.            //ErrorAlert( rErrorStrs, eCantWriteFile );
  472.         return result ;        
  473.     }
  474.  
  475.        // now get rid of the picture handle 
  476.     HUnlock( (Handle) (Handle)myPic ) ;
  477.        KillPicture(myPic);
  478.  
  479. }
  480.  
  481.  
  482. //--------------------------------------------------------------------------------------
  483. //                    
  484. //    Name            DoSaveAs
  485. //
  486. //    Arguments        WindowPtr window - a reference to the window whos content to save
  487. //
  488. //    Description        This is a high level routine that allows the user to save the
  489. //                    current window in a compressed format.
  490. //    
  491. //                    The principle at work here is to factor the code as far as 
  492. //                    is possible.  This means lots of small functions.  why??
  493. //                    If we seperate functionality out from user interface code,
  494. //                    it becomes easier to provide support for, say, scripting at 
  495. //                    a later date.
  496. //
  497. //    Returns            nothing.  This one is a bit of a do or die routine.  The 
  498. //                    functions that this routine calls will perform error handling.
  499. //
  500. //    Side Effects    none directly, but the routines that this calls may allocate storage
  501. //
  502. //--------------------------------------------------------------------------------------
  503.  
  504. void DoSaveAs( WindowPtr window ) 
  505. {
  506.     SCParams            params;
  507.     ComponentResult        result = noErr;
  508.     Point                where;
  509.     ComponentInstance    ci = nil;
  510.     SFTypeList            typeList;
  511.     SFReply                inReply;
  512.     SFReply                outReply;
  513.     short                srcPictFRef = 0;
  514.     short                dstPictFRef = 0;
  515.     PicHandle            myPic ;
  516.     
  517.     long        maxCompressedSize = 0;
  518.     OSErr        theErr;
  519.     Handle                    compressedDataH    ;
  520.     Ptr                        data ;
  521.     ImageDescriptionHandle ImageDescH ;
  522.     Rect                theRect ;
  523.  
  524.  
  525.     GWorldPtr            theNewWorld ;
  526.     PixMapHandle        offPixMap ;
  527.  
  528.     
  529.     // get the GWorld from the window refcon
  530.     if((theNewWorld = (GWorldPtr)GetWRefCon ( window )) == nil )
  531.         return ;
  532.         
  533.     if((offPixMap = GetGWorldPixMap( theNewWorld )) == nil)
  534.         return ;
  535.  
  536.     
  537.     ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType);
  538.     if (ci == nil) 
  539.         return ;
  540.         
  541.     // initialise for standard parameters
  542.     
  543.     InitialiseSCParams(¶ms) ;
  544.     
  545.     // get the user to choose the compression settings, use the
  546.     // standard compression dialog to ensure consistency
  547.     result = GetCompressionSettings( window, ¶ms, ci ) ;
  548.  
  549.     // check to see if the user canceled, or if an error has occurred
  550.     if (result != noErr ) {
  551.     
  552.         if( result != 1 ) {        // if user hit cancel, bail but don't generate error.
  553.  
  554.             // This handles the case where an error occurred
  555.             // ErrorAlert( rErrorStrs, eCantGetCompressionSettings );
  556.  
  557.         }
  558.         return ;
  559.         
  560.     }
  561.     
  562.     // get an open a file for writing
  563.     if( (theErr = GetOutputFileRef( &dstPictFRef )) != noErr ) {
  564.         return ;
  565.     } 
  566.         
  567.     
  568.     theRect = (**offPixMap).bounds ;
  569.     
  570.     // compress the PixMap here…
  571.     myPic =  GetQTCompressedPict( offPixMap, ¶ms ) ;
  572.  
  573.  
  574.     if( myPic == nil ) {
  575.         FSClose(dstPictFRef);
  576.         CloseComponent(ci);
  577.         return ;
  578.     }
  579.     else {
  580.         if(WritePict( myPic, dstPictFRef ) != noErr ) {
  581.             FSClose(dstPictFRef);
  582.             CloseComponent(ci);
  583.             return ;
  584.         }
  585.     
  586.     }
  587.     
  588.     FSClose(dstPictFRef);
  589.  
  590.     // clean up and go…
  591.                 
  592.     CloseComponent(ci);
  593.     
  594.     return ;
  595.  
  596. }
  597.  
  598.